#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include "Mutex.hpp"

static int gcap = 10;

namespace BlockQueueModule
{
    template <typename T>
    class BlockQueue
    {
        bool IsFull() { return _q.size() == _cap; }
        bool IsEmpty() { return _q.empty(); }

    public:
        BlockQueue(int cap = gcap) : _cap(cap), _cwait_num(0), _pwait_num(0)
        {
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_productor_cond, nullptr);
            pthread_cond_init(&_consumer_cond, nullptr);
        }

        // 生产者生产数据
        void Equeue(const T &in)
        {
            pthread_mutex_lock(&_mutex);
            // 1.生产数据是你想生产就可以生产吗？不是的，生产数据是有条件的
            // 结论：在临界区等待是必然的(目前的)
            while (IsFull()) // 对条件进行判断，为了防止发生伪判断，进行循环判断
            {
                std::cout << "生产者进行等待" << std::endl;
                _pwait_num++;

                // 1. pthread_cond_wait调用是:
                // a. 让调用线程等待 b. 自动释放曾经持有的_mutex锁 c. 当条件满足，线程唤醒，pthread_cond_wait要求线性
                // 必须重新竞争_mutex锁，竞争成功，方可返回！！！
                // 之前：安全
                pthread_cond_wait(&_productor_cond, &_mutex); // wait的时候是一定是持有锁的，wait的时候，会释放锁
                // 只要等待，必定会有唤醒，唤醒的时候，就要继续从这个位置向下运行！

                _pwait_num--;
                
                std::cout << "生产者醒来了" << std::endl;
            }

            // IsFull不满足或者线程醒过来了
            _q.push(in); // 生产

            if (_cwait_num) // 此时肯定有数据，可以唤醒等待队列里面的消费者
            {
                std::cout << "唤醒消费者" << std::endl;
                pthread_cond_signal(&_consumer_cond);
            }

            // 退出临界区，释放锁
            pthread_mutex_unlock(&_mutex);
        }

        // 消费者消费数据
        void Pop(T *out)
        {
            pthread_mutex_lock(&_mutex);

            while (IsEmpty())
            {
                std::cout << "消费者进行等待" << std::endl;

                _cwait_num++;
                pthread_cond_wait(&_consumer_cond, &_mutex); // wait的时候是一定是持有锁的，wait的时候，会释放锁
                _cwait_num--;

                std::cout << "消费者醒来了" << std::endl;
            }

            *out = _q.front();
            _q.pop();

            if (_pwait_num) // 生产者消费数据了，可以让生产者生产了
            {
                std::cout << "唤醒生产者" << std::endl;
                pthread_cond_signal(&_productor_cond);
            }

            pthread_mutex_unlock(&_mutex);
        }

        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_productor_cond);
            pthread_cond_destroy(&_consumer_cond);
        }

    private:
        std::queue<T> _q;               // 保存数据的容器：临界资源
        int _cap;                       // 最大容量
        pthread_mutex_t _mutex;         // 互斥量
        pthread_cond_t _productor_cond; // 消费者条件变量
        pthread_cond_t _consumer_cond;  // 生产者条件变量

        int _cwait_num;
        int _pwait_num;
    };
}